
import * as THREE from 'three';
import * as CANNON from 'cannon-es';
import CannonDebugger from 'cannon-es-debugger';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

let canvas = document.getElementById("canvas") as HTMLCanvasElement;
let stage = new THREE.Object3D();

let boxes: THREE.Mesh[] = [];
let plane: THREE.Mesh;
let cannonDebugger: CannonDebugger;
let physicsWorld: CANNON.World;

let camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 20;

let scene = new THREE.Scene();
scene.add(stage);

let renderer = new THREE.WebGLRenderer({ antialias: true, canvas });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight); renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap

const controls = new OrbitControls(camera, renderer.domElement);
controls.minDistance = 1;
controls.maxDistance = 1000;
controls.update();


setupPhysics();
addViews();

animate();

function setupPhysics() {
    physicsWorld = new CANNON.World({
        gravity: new CANNON.Vec3(0, -9.82, 0),
    });
    physicsWorld.broadphase = new CANNON.SAPBroadphase(physicsWorld);
    physicsWorld.defaultContactMaterial.friction = 0.2;
    cannonDebugger = new CannonDebugger(scene, physicsWorld);
}

function addViews() {
    const size = { width: 1, height: 1, depth: 1 };
    const boxGeometry = new THREE.BoxGeometry(size.width, size.height, size.depth);
    const boxMaterial = new THREE.MeshNormalMaterial();
    const boxOne = new THREE.Mesh(boxGeometry, boxMaterial);

    const chassisShape = new CANNON.Box(new CANNON.Vec3(size.width / 2, size.height / 2, size.depth / 2))

    for (let i = 0; i < 100; i++) {
        const box = boxOne.clone();
        const chassisBody = new CANNON.Body({ mass: 1 })
        chassisBody.addShape(chassisShape);
        chassisBody.position.set(getRandom(-10, 10), getRandom(3, 20), getRandom(-10, 10));
        // chassisBody.angularVelocity.set(0, 0.5, 0);

        physicsWorld.addBody(chassisBody);
        box.userData = { body: chassisBody };
        stage.add(box);

        boxes.push(box)
    }

    const planeGeometry = new THREE.PlaneGeometry(10, 10);
    const planeMaterial = new THREE.MeshNormalMaterial();
    plane = new THREE.Mesh(planeGeometry, planeMaterial);


    const planeShape = new CANNON.Plane()
    const planeBody = new CANNON.Body({ mass: 0 })
    planeBody.addShape(planeShape);

    planeBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI * 0.5);
    planeBody.position.set(0, -4, 0);

    physicsWorld.addBody(planeBody);
    plane.userData = { body: planeBody };
    stage.add(plane);
}

function getRandom(a:number, b:number, isInt = false) {
    if (isInt) {
        return Math.floor(Math.random() * (b - a + 1)) + a;
    } else {
        return Math.random() * (b - a) + a;
    }
}

function animate() {
    physicsWorld.fixedStep();
    cannonDebugger.update();

    boxes.forEach(box => {
        box.position.copy(box.userData.body.position);
        box.quaternion.copy(box.userData.body.quaternion);
    })


    plane.position.copy(plane.userData.body.position);
    plane.quaternion.copy(plane.userData.body.quaternion);


    renderer.render(scene, camera);
    requestAnimationFrame(animate);
}
